﻿#region Copyright information
// 
// Copyright © 2012-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the SubnetworkToolkit and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: Program.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@outlook.com
// - Date Created: 2013-01-30 2:55 PM
// - Last Modified: 2013-01-30 3:21 PM
// 
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using BOL.Algorithms.Clustering;
using BOL.Linq.Distances;
using CommandLine;
using SubnetworkToolkit.IO;

namespace SubnetworkToolkit.Cluster
{
    static class Program
    {
        static void Main(string[] args)
        {
            var options = new Options();

            try
            {
                // parse arguments
                var parser = new CommandLineParser(new CommandLineParserSettings(Console.Error));
                parser.ParseArguments(args, options);

                if (args.Length == 0 || args[0] == "-h")
                    Console.Error.Write(options.GetUsage());
                else
                {
                    var gmt = options.GmtFile;
                    var gr = new GmtReader(gmt);
                    var geneSets = gr.Read();
                    gr.Close();

                    var depth = options.Depth;

                    var metric = options.Metric;
                    Func<IEnumerable<bool>, IEnumerable<bool>, double> func = Disimilarity.Manhattan;
                    switch (metric)
                    {
                        case DistanceMetric.BrayCurtis: func = Disimilarity.BrayCurtis; break;
                        case DistanceMetric.Chord: func = Disimilarity.Chord; break;
                        case DistanceMetric.Dot: func = Disimilarity.Dot; break;
                        case DistanceMetric.Euclidean: func = Disimilarity.Euclidean; break;
                        case DistanceMetric.Hellinger: func = Disimilarity.Hellinger; break;
                        case DistanceMetric.Intersection: func = Disimilarity.Intersection; break;
                        case DistanceMetric.Manhattan: func = Disimilarity.Manhattan; break;
                        case DistanceMetric.MeanManhattan: func = Disimilarity.MeanManhattan; break;
                        case DistanceMetric.PatternDifference: func = Disimilarity.PatternDifference; break;
                        case DistanceMetric.ShapedDifference: func = Disimilarity.ShapedDifference; break;
                        case DistanceMetric.SizedDifference: func = Disimilarity.SizedDifference; break;
                        case DistanceMetric.Vari: func = Disimilarity.Vari; break;
                    }

                    var isVerbose = options.IsVerbose;

                    var linkageCriterion = options.LinkageCriterion;
                    var uniqueGenes = new HashSet<int>();
                    foreach (var gene in geneSets.SelectMany(geneSet => geneSet.Genes))
                        uniqueGenes.Add(gene.Id);
                    var binaryGeneSets = geneSets.Select(x => new BinaryGeneSet(x.Name, x.Description, x.Genes, BinarySelector(uniqueGenes, x.Genes.Select(y => y.Id))));
                    var hc = new HierarchicalClusterer<bool>(func, linkageCriterion);
                    var rootNode = hc.Cluster(binaryGeneSets, x => x.Points);
                    PrintNode(rootNode, depth);
                }
            }
            catch (Exception ex)
            {
                Console.Error.Write(ex.Message);
            }
        }

        static bool[] BinarySelector(IEnumerable<int> uniqueGenes, IEnumerable<int> genes)
        {
            return uniqueGenes.Select(genes.Contains).ToArray();
        }

        static void PrintNode(IDendrogramNode<BinaryGeneSet> node, int depth)
        {
            if (node.Depth == depth)
                Console.WriteLine("{0}\t{1}", node, String.Join(", ", node.Select(x => x.Name)));
            else if (node.Depth < depth)
                foreach (var child in node.Children)
                    PrintNode(child, depth);
        }
    }
}
